﻿
#include "model.h"

inline void RaiseException()
{
    __asm int 3;
}

template<class S>
inline void SkipToken(ifstream& file, string& buf, S& token)
{
    file >> buf;
    if(buf != string(token))
        RaiseException();
}

template<class S,
    class T>
inline void ReadTokenVal(ifstream& file, string& buf, S& token, T& val) 
{
    SkipToken(file, buf, token);
    file >> val;
}

void CModel::createVbo(uint8 mn)
{
    // tymczasowa tablica przechowująca dane wierzchołków
	vector<SVertexFormat> vvbo;
	
    // stworzenie tymczasowej tablicy indeksów
	vector<uint16> vvboi;

    for(uint it = 0; it < m_meshes[mn].t.size(); it++)
        // to samo dla 3 wierzchołków
        for(uint it2 = 0; it2 < 3; it2++)
		{			
			vector<float3> &v = m_meshes[mn].v,
				&n = m_meshes[mn].n, 
				&c = m_meshes[mn].c;
			vector<SPoly>& t = m_meshes[mn].t;

			SVertexFormat tvert = {v[t[it].v[it2]].x,
				v[t[it].v[it2]].y,
				v[t[it].v[it2]].z,
				n[t[it].n[it2]].x,
				n[t[it].n[it2]].y,
				n[t[it].n[it2]].z,
				c[t[it].c[it2]].u,
				c[t[it].c[it2]].v};

			// sprawdzenie, czy wierzchołek nie występuje już w tablicy -
			// jeżeli tak, to nie będziemy go dublować, a podmienimy indeks 
			// w tablicy indeksów
			vector<SVertexFormat>::iterator found = find(vvbo.begin(), vvbo.end(), tvert);
			if(found == vvbo.end())
			{
				// indeks wierzchołka
				uint16 index = it * 3 + it2;
				vvboi.push_back(index);
			}
			else
				vvboi.push_back(found - vvbo.begin());
		}

    uint16* tvboi = new uint16[m_meshes[mn].t.size() * 3]; 
    GLfloat* tvbo = new GLfloat[m_meshes[mn].t.size() * 3 * 8]; 

    // dla każdego trójkąta wpisz w VBO odpowiednie dane znajdujące się w
    // tablicach wierzchołków, normalnych i koordynatów tekstury
    // pod danymi z tablicy trójkątów indeksami
    // offset między kolejnymi wierszami tvbo wynosi 8 floatów,
    // pomiędzy trójkatami 24 floaty
    for(uint it = 0; it < m_meshes[mn].t.size(); it++)
        // to samo dla 3 wierzchołków
        for(uint it2 = 0; it2 < 3; it2++)
        {
			vector<float3> &v = m_meshes[mn].v,
				&n = m_meshes[mn].n, 
				&c = m_meshes[mn].c;
			vector<SPoly>& t = m_meshes[mn].t;

            // pozycja: x, y, z
            tvbo[it * 24 + it2 * 8 + 0] = v[t[it].v[it2]].x;
            tvbo[it * 24 + it2 * 8 + 1] = v[t[it].v[it2]].y;
            tvbo[it * 24 + it2 * 8 + 2] = v[t[it].v[it2]].z;
            // normalna: x, y, z
            tvbo[it * 24 + it2 * 8 + 3] = n[t[it].n[it2]].x;
            tvbo[it * 24 + it2 * 8 + 4] = n[t[it].n[it2]].y;
            tvbo[it * 24 + it2 * 8 + 5] = n[t[it].n[it2]].z;
            // koordynaty tekstury: u, v
            tvbo[it * 24 + it2 * 8 + 6] = c[t[it].c[it2]].u;
            tvbo[it * 24 + it2 * 8 + 7] = c[t[it].c[it2]].v;
        }
   
    // stworzenie VBO dla wierzchołków i przesłanie danych
    glGenBuffers(1, &m_meshes[mn].vbo); 
    glBindBuffer(GL_ARRAY_BUFFER, m_meshes[mn].vbo);
    glBufferData(GL_ARRAY_BUFFER, m_meshes[mn].t.size() * 3 * 8 * sizeof(GLfloat), 
        tvbo, GL_STATIC_DRAW);
    // można usunąć tablicę tvbo
    delete[] tvbo;

    for(uint it = 0; it < m_meshes[mn].t.size() * 3; it++)
        // wiersze tablicy wierzchołków były podawane zgodnie z kolejnością
        // w tablicy opisu trójkątów, więc indeksy będą po prostu
        // kolejnymi liczbami
        tvboi[it] = it;

    // stworzenie VBO dla indeksów i przesłanie danych
    glGenBuffers(1, &m_meshes[mn].vboi); 
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_meshes[mn].vboi);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_meshes[mn].t.size() * 3 * sizeof(uint16), tvboi, GL_STATIC_DRAW);
    // można usunąć tablicę tvboi
    delete[] tvboi;
}

void CModel::createVboAnim(uint8 mn)
{
    // dla każdego trójkąta wpisz w VBO odpowiednie dane znajdujące się w
    // tablicach wierzchołków, normalnych i koordynatów tekstury
    // pod danymi z tablicy trójkątów indeksami
    // offset między kolejnymi wierszami tvbo wynosi 16 floatów,
    // pomiędzy trójkatami 48 floatów

	// tymczasowa tablica przechowująca dane wierzchołków
	vector<SVertexAFormat> vvbo;	
    // stworzenie tymczasowej tablicy indeksów
	vector<uint16> vvboi;

    for(uint it = 0; it < m_meshes[mn].t.size(); it++)
        // to samo dla 3 wierzchołków
        for(uint it2 = 0; it2 < 3; it2++)
		{			
			vector<float3> &v = m_meshes[mn].v,
				&n = m_meshes[mn].n, 
				&c = m_meshes[mn].c,
				&m = m_meshes[mn].m,
				&w = m_meshes[mn].wm;
			vector<SPoly>& t = m_meshes[mn].t;
			
            // może się zdażyć, że na jedną kość będzie wpływało więcej niż 4 macierze
            // wtedy suma wag czterech pierwszych (branych pod uwagę) będzie różna od 1.0,
            // dlatego należy znormalizować te wagi 
            float sum = w[t[it].v[it2]].x + w[t[it].v[it2]].y + 
				w[t[it].v[it2]].z + w[t[it].v[it2]].w;

			SVertexAFormat tvert = {v[t[it].v[it2]].x,
				v[t[it].v[it2]].y,
				v[t[it].v[it2]].z,
				n[t[it].n[it2]].x,
				n[t[it].n[it2]].y,
				n[t[it].n[it2]].z,
				c[t[it].c[it2]].u,
				c[t[it].c[it2]].v,
				m[t[it].v[it2]].x,
				m[t[it].v[it2]].y,
				m[t[it].v[it2]].z,
				m[t[it].v[it2]].w,
				w[t[it].v[it2]].x / sum,
				w[t[it].v[it2]].y / sum,
				w[t[it].v[it2]].z / sum,
				w[t[it].v[it2]].w / sum};

			// sprawdzenie, czy wierzchołek nie występuje już w tablicy -
			// jeżeli tak, to nie będziemy go dublować, a podmienimy indeks 
			// w tablicy indeksów
			vector<SVertexAFormat>::iterator found = find(vvbo.begin(), vvbo.end(), tvert);
			if(found == vvbo.end())
			{
				// indeks wierzchołka
				vvbo.push_back(tvert);
				vvboi.push_back(0); //vvbo.size() - 1);
			}
			else
			{
				uint ti = found - vvbo.begin();
				vvboi.push_back(0); //found - vvbo.begin());
			}
		}

	GLfloat* tv = new GLfloat[vvbo.size() * 16 * sizeof(GLfloat)];
	memcpy(tv, &vvbo[0], vvbo.size() * 16 * sizeof(GLfloat));

	uint16* tvi = new uint16[m_meshes[mn].t.size() * 3 * sizeof(uint16)];
	memcpy(tvi, &vvboi[0], m_meshes[mn].t.size() * 3 * sizeof(uint16));

    // stworzenie VBO dla wierzchołków i przesłanie danych
    glGenBuffers(1, &m_meshes[mn].vbo); 
    glBindBuffer(GL_ARRAY_BUFFER, m_meshes[mn].vbo);
    glBufferData(GL_ARRAY_BUFFER, vvbo.size() * 16 * sizeof(GLfloat), 
        tv, GL_STATIC_DRAW);

    // stworzenie VBO dla indeksów i przesłanie danych
    glGenBuffers(1, &m_meshes[mn].vboi); 
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_meshes[mn].vboi);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_meshes[mn].t.size() * 3 * sizeof(uint16), 
		tvi, GL_STATIC_DRAW);
}

void CModel::deleteVbo(uint8 mn)
{
//    glDeleteBuffers(1, this);
}

CModel::CModel(const string& fileName)
{  
    // sprawdzenie obecności pliku
    ifstream file(fileName.c_str(), ifstream::in);
    if(file.fail())
        RaiseException();

    // wczytanie pliku
    string buf, stmp;
    int itmp, itmp2;

    file >> buf;   // nazwa modelu
    SkipToken(file, buf, "{");
    ReadTokenVal(file, buf, "meshes", itmp);  // liczba mesh'ów
    
    m_meshes.resize(itmp);
    for(uint it = 0; it < m_meshes.size(); it++)
    {
        SMesh& itm = m_meshes[it];

        SkipToken(file, buf, "mesh");
        SkipToken(file, buf, "{");

        ReadTokenVal(file, buf, "material", stmp);  // wczytaj nazwę pliku z materiałem
        ReadTokenVal(file, buf, "weights", itmp);   // wczytaj liczbę wag 
        itm.wj.resize(itmp);
        ReadTokenVal(file, buf, "vertices", itmp);  // wczytaj liczbę wierzchołków 
        itm.v.resize(itmp);
        itm.m.resize(itmp);
        itm.wm.resize(itmp);
        ReadTokenVal(file, buf, "normals", itmp);   // wczytaj liczbę normalnych
        itm.n.resize(itmp);
        ReadTokenVal(file, buf, "matcoords", itmp); // wczytaj liczbę wsp. materiału
        itm.c.resize(itmp);
        ReadTokenVal(file, buf, "triangles", itmp); // wczytaj liczbę trójkątów
        itm.t.resize(itmp);

        // wczytaj wagi
        for(uint it2 = 0; it2 < itm.wj.size(); it2++)
        {
            SkipToken(file, buf, "w");
            SkipToken(file, buf, "{");
            file >> itm.wj[it2].id;
            file >> itm.wj[it2].val;
            SkipToken(file, buf, "}");
        }

        // wczytaj wierzchołki
        for(uint it2 = 0; it2 < itm.v.size(); it2++)
        {
            SkipToken(file, buf, "v");
            SkipToken(file, buf, "{");

            // wcztanie początkowego indeksu macierzy kości 
            // przypisanej do tego wierzchołka
            file >> itmp;       
            // wcztanie ilości macierzy które mogą na raz wpływać
            // na wierzchołek.
            // w tym formacie takich macierzy może być maksymalnie 4
            file >> itmp2;

            float sum = 0.0f;
            for(int s = 0; s < itmp2; s++)
                sum += itm.wj[itmp + s].val;
            if(sum > 1.0f)
            {
                printf("%f \n", sum);
                for(int s = 0; s < itmp2; s++)
                    itm.wj[itmp + s].val /= sum;
            }

            if(itmp2 > 0) 
            {
                itm.m[it2].x = itm.wj[itmp].id;     // indeks kości (macierzy)
                itm.wm[it2].x = itm.wj[itmp].val;   // waga kości
            }
            if(itmp2 > 1) 
            {
                itm.m[it2].y = itm.wj[itmp + 1].id;
                itm.wm[it2].y = itm.wj[itmp + 1].val;
            }
            if(itmp2 > 2) 
            {
                itm.m[it2].z = itm.wj[itmp + 2].id;
                itm.wm[it2].z = itm.wj[itmp + 2].val;
            }
            if(itmp2 > 3) 
            {
                itm.m[it2].w = itm.wj[itmp + 3].id;
                itm.wm[it2].w = itm.wj[itmp + 3].val;
            }

            // wczytanie pozycji wierzchołka
            SkipToken(file, buf, "{");
            file >> itm.v[it2].x;
            file >> itm.v[it2].y;
            file >> itm.v[it2].z;
            SkipToken(file, buf, "}");
            SkipToken(file, buf, "}");
        }

        // wczytaj normalne
        for(uint it2 = 0; it2 < itm.n.size(); it2++)
        {
            SkipToken(file, buf, "n");
            SkipToken(file, buf, "{");
            file >> itm.n[it2].x;
            file >> itm.n[it2].y;
            file >> itm.n[it2].z;
            SkipToken(file, buf, "}");
        }

        // wczytaj koordynaty tekstur
        for(uint it2 = 0; it2 < itm.c.size(); it2++)
        {
            SkipToken(file, buf, "c");
            SkipToken(file, buf, "{");
            file >> itm.c[it2].u;
            file >> itm.c[it2].v;
            SkipToken(file, buf, "}");
        }

        // wczytaj opis trójkątów
        for(uint it2 = 0; it2 < itm.t.size(); it2++)
        {
            SkipToken(file, buf, "t");
            SkipToken(file, buf, "{");
            SkipToken(file, buf, "v");
            SkipToken(file, buf, "{");
            file >> itm.t[it2].v[0];
            file >> itm.t[it2].v[1];
            file >> itm.t[it2].v[2];
            SkipToken(file, buf, "}");
            SkipToken(file, buf, "n");
            SkipToken(file, buf, "{");
            file >> itm.t[it2].n[0];
            file >> itm.t[it2].n[1];
            file >> itm.t[it2].n[2];
            SkipToken(file, buf, "}");
            SkipToken(file, buf, "c");
            SkipToken(file, buf, "{");
            file >> itm.t[it2].c[0];
            file >> itm.t[it2].c[1];
            file >> itm.t[it2].c[2];
            SkipToken(file, buf, "}");
            SkipToken(file, buf, "}");
        }
    }

    SkipToken(file, buf, "}");

    // wczytaj bazowy układ szkieletu
    SkipToken(file, buf, "basepose");
    SkipToken(file, buf, "{");
    ReadTokenVal(file, buf, "joints", (uint16&)m_njoints);  // liczba kości
    m_poses.j.resize(m_njoints);

    for(uint it = 0; it < m_poses.j.size(); it++)
    {
        SkipToken(file, buf, "j");
        SkipToken(file, buf, "{");

        // wczytaj wszystkie 16 elementów macierzy kości
        for(uint it2 = 0; it2 < 16; it2++)
            file >> m_poses.j[it].m[it2];

        SkipToken(file, buf, "}");
    }

    SkipToken(file, buf, "}");
    SkipToken(file, buf, "}");

    return;
}

CModel::~CModel()
{
}


CAnim::CAnim(const string& fileName)
{
    // sprawdzenie obecności pliku
    ifstream file(fileName.c_str(), ifstream::in);
    if(file.fail())
        RaiseException();

    // wczytanie pliku
    string buf, stmp;
    int itmp, itmp2;

    file >> buf;    // nazwa zestawu animacji
    SkipToken(file, buf, "{");
    ReadTokenVal(file, buf, "animations", itmp);                        // liczba animacji
    m_animations.resize(itmp);
    ReadTokenVal(file, buf, "joints", (uint16&)m_njoints);              // liczba kości
    
    for(uint it = 0; it < m_animations.size(); it++)
    {
        SkipToken(file, buf, "animation");
        SkipToken(file, buf, "{");

        ReadTokenVal(file, buf, "name", m_animations[it].name);         // nazwa animacji
        ReadTokenVal(file, buf, "frames", m_animations[it].nframes);    // liczba klatek
        m_animations[it].frames.resize(m_animations[it].nframes);    
        // prędkość animacji
        ReadTokenVal(file, buf, "framerate", m_animations[it].framerate);  

        for(uint it2 = 0; it2 < m_animations[it].nframes; it2++)
        {
            m_animations[it].frames[it2].j.resize(m_njoints);

            SkipToken(file, buf, "frame");
            SkipToken(file, buf, "{");

            for(uint it3 = 0; it3 < m_njoints; it3++)
            {
                SkipToken(file, buf, "j");
                SkipToken(file, buf, "{");

                // wczytaj wszystkie 16 elementów macierzy kości
                for(uint it4 = 0; it4 < 16; it4++)
                    file >> m_animations[it].frames[it2].j[it3].m[it4];

                SkipToken(file, buf, "}");
            }
            SkipToken(file, buf, "}");
        }
        SkipToken(file, buf, "}");
    }
}

CAnim::~CAnim()
{
};

